using System.Collections.Generic;
using InfiniTec.Data;

namespace InfiniTec.Expressions
{
    public class BetweenExpression<T>: CompareExpressionBase<T>, IBetweenExpression
    {
        private T _UpperValue;
        private BetweenBoundaryInclusion _BoundaryInclusion;

        IPropertyDefinition IBetweenExpression.PropertyDefinition { get { return PropertyDefinition; } }
        object IBetweenExpression.LowerValue { get { return Value; } }
        object IBetweenExpression.UpperValue { get { return _UpperValue; } }

        /// <summary>
        /// Gets or sets the upper value for the between operation.
        /// </summary>
        /// <value>The upper value.</value>
        public T UpperValue
        {
            get { return _UpperValue; }
            set { _UpperValue = value; }
        }

        /// <summary>
        /// Gets or sets the boundary inclusion.
        /// </summary>
        /// <value>The boundary inclusion.</value>
        public BetweenBoundaryInclusion BoundaryInclusion
        {
            get { return _BoundaryInclusion; }
            set { _BoundaryInclusion = value; }
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="BetweenExpression&lt;TValue&gt;"/> class.
        /// </summary>
        /// <param name="property">The property definition for this expression.</param>
        /// <param name="lowerValue">The lower value.</param>
        /// <param name="upperValue">The upper value.</param>
        /// <param name="boundaryInclusion">The boundary inclusion.</param>
        public BetweenExpression(PropertyDefinition<T> property, T lowerValue, T upperValue, BetweenBoundaryInclusion boundaryInclusion) : base(property, lowerValue)
        {
            _UpperValue = upperValue;
            _BoundaryInclusion = boundaryInclusion;
        }


        protected override bool Evaluate(T value, T actualValue)
        {
            Comparer<T> comparer;
            int lowerComparison;
            int upperComparison;

            comparer = Comparer<T>.Default;

            lowerComparison = comparer.Compare(actualValue, Value);
            upperComparison = comparer.Compare(actualValue, UpperValue);

            if (lowerComparison == -1) return false;
            if (upperComparison == 1) return false;

            if ((lowerComparison == 0 && (BoundaryInclusion & BetweenBoundaryInclusion.LowerValue) == BetweenBoundaryInclusion.LowerValue) ||
                (upperComparison == 0 && (BoundaryInclusion & BetweenBoundaryInclusion.UpperValue) == BetweenBoundaryInclusion.UpperValue))
            {
                return true;
            }

            if (lowerComparison == 0 || upperComparison == 0)
            {
                return false;
            }

            return true;
        }


        public Expression Explode()
        {
            Expression lowerBoundary;
            Expression upperBoundary;

            if ((BoundaryInclusion &  BetweenBoundaryInclusion.LowerValue) == BetweenBoundaryInclusion.LowerValue)
            {
                lowerBoundary = GreaterOrEqual(PropertyDefinition, Value);
            }
            else
            {
                lowerBoundary = Greater(PropertyDefinition, Value);
            }
            if ((BoundaryInclusion & BetweenBoundaryInclusion.UpperValue) == BetweenBoundaryInclusion.UpperValue)
            {
                upperBoundary = SmallerOrEqual(PropertyDefinition, UpperValue);
            }
            else
            {
                upperBoundary = Smaller(PropertyDefinition, UpperValue);
            }
            return And(lowerBoundary, upperBoundary);
        }
    }
}
